Exploiting generic schemas
While
I've been preaching so far about all the benefits and capabilities of
structured schemas, there are real cases where you need to be
exceedingly flexible in the way you take data in. You may want to open
up an inbound on-ramp that takes any sort of message in, and let the
message bus figure out how to type it and route it. Or a particular
message type may have a portion that expects variable content and you
wish to accommodate any XML structure.
First,
let's look at how to make a part of a specific schema expect generic
content, and see what the resulting WCF service endpoint would look
like. We can add generic placeholders to an XML schema through the use
of the XSD "any" node type. There are two key attributes of the any node which dictate how the XML parser treats the content embedded here. The Namespace property can contain any of these values:
Value
|
Description
|
---|
##any
|
The default value; this means that the XML content can be part of any document namespace.
|
##targetNamespace
|
XML content must be associated with target namespace of the document.
|
##local
|
Data not associated with any namespace are allowed
|
##other
|
XML content associated with other namespace besides the target
|
List of values
|
Data can be associated with any of the comma separated namespace values
|
You have a variety of choices for how to qualify the XML content with a namespace. However, the second attribute of the any node is just as vital as the namespace instruction. The Process Contents property specifies how an XML parser should evaluate the XML content in the any node.
Value
|
Description
|
---|
Strict
|
The default value, this means that the parser will look at the
namespace of the XML content and must find a corresponding schema for
validation |
Lax
|
The parser tries to find the schema and validate the XML content, but if it fails, it won't raise an exception
|
Skip
|
The parser ignores the XML content and does not try to validate it
|
So if you have a namespace value of ##any and set the Process Contents to Skip,
you can send along virtually any XML structure you'd like. Of course,
you may still want to provide a bit of verification by limiting the
allowable namespaces or still requiring the BizTalk parser to find a
schema that matches the generic payload.
I went ahead and changed our Enrollment schema by adding a new SiteSpecificData record which has an any
element underneath it. If my solution simply took this content and
crammed it into a database field for archival purposes, I could be
confidently lazy and accept any namespace and skip schema validation on
that generic node.
We
can then publish this schema as a WCF endpoint and see how service
consumers would interact with this open payload. The code in the
auto-generated client object shows that the generic node expects
nothing but an XmlElement object.
public partial class Enrollment{
service-oriented schema patterngeneric schemas, exploitingprivate string studyIDField;
private string siteIDField;
private string trialIDField;
private Subject subjectField;
private System.Xml.XmlElement siteSpecificDataField;
}
This
is a fairly useful technique when you want to develop schemas that are
expected to handle a variety of yet-unknown clients in the future.
The
situation may also arise when you wish to make the entire payload of
the service generic. That is, the service is capable of accepting any
content, and the message bus is responsible for figuring out what to do
with it. This is one situation where you cannot use the Publish schemas as WCF service option in the BizTalk WCF Service Publishing Wizard.
You may recall my earlier statement that there is virtually no need to
ever expose orchestration as services instead of exposing schemas.
Well, here is that one fringe exception to that statement.
The publish schemas option of the BizTalk WCF Service Publishing Wizard
always expects us to choose a schema from an existing assembly. We
cannot choose simple types, or generic types. Hence, we need to cheat
and build a temporary orchestration that can force the wizard to build
a service just how we want it. We start out by creating a new
orchestration and adding a message of type System.Xml.XmlDocument. Next we put a single Receive shape connected to a one-way logic receive port. The "finished" orchestration looks like this:
After deploying the updated solution, we should walk through the BizTalk WCF Service Publishing Wizard and choose to publish an orchestration port as a service.
The WCF service reference in the client application defines a request message that accepts a generic object as its input.
public partial class PublishAnyTrialEventRequest {
[MessageBodyMemberAttribute(Namespace="", Order=0)]
public object part;
public PublishAnyTrialEventRequest() {
}
public PublishAnyTrialEventRequest(object part) {
this.part = part;
}
}
Clearly
you have to use this pattern with caution, as you must be confident
that these messages are expected by the message bus and that you know
how to handle each and every one that arrives. Also, your service
consumer has absolutely no direction as to the shape of the data that
the service expects and must use some other means to inflate the
request message.